home *** CD-ROM | disk | FTP | other *** search
/ MacWorld 2003 August / MW 8 2003 CD1.iso / Inside Macworld / Product News / gimp-1.2.4.sit / gimp-1.2.4 / plug-ins / common / gee.c < prev    next >
Encoding:
C/C++ Source or Header  |  2000-12-19  |  17.9 KB  |  845 lines

  1. /*
  2.  * (c) Adam D. Moss : 1998-2000 : adam@gimp.org : adam@foxbox.org
  3.  *
  4.  * Enjoy.
  5.  */
  6.  
  7. /*
  8.  * Version 1.01 : 2000-12-12
  9.  *
  10.  */
  11. #include "config.h"
  12.  
  13. #include <stdlib.h>
  14. #include <stdio.h>
  15. #include <string.h>
  16. #include <ctype.h>
  17. #include <time.h>
  18.  
  19. #include <gtk/gtk.h>
  20.  
  21. #include <libgimp/gimp.h>
  22. #include <libgimp/gimpui.h>
  23.  
  24. #include "libgimp/stdplugins-intl.h"
  25.  
  26.  
  27. /* Is the plug-in hidden?  Hey, if you can read this, you may
  28.    as well comment-out the next line...! */
  29. #define HIDDEN
  30.  
  31.  
  32. /* Declare local functions. */
  33. static void query (void);
  34. static void run   (gchar      *name,
  35.            gint        nparams,
  36.            GimpParam  *param,
  37.            gint       *nreturn_vals,
  38.            GimpParam **return_vals);
  39.  
  40. static void do_fun            (void);
  41.  
  42. static gint window_delete_callback (GtkWidget *widget,
  43.                     GdkEvent  *event,
  44.                     gpointer   data);
  45. static void window_close_callback  (GtkWidget *widget,
  46.                     gpointer   data);
  47. static gint iteration_callback          (gpointer   data);
  48. static void toggle_feedbacktype    (GtkWidget *widget,
  49.                     gpointer   data);
  50.  
  51. static void render_frame           (void);
  52. static void init_preview_misc      (void);
  53.  
  54.  
  55. GimpPlugInInfo PLUG_IN_INFO =
  56. {
  57.   NULL,  /* init_proc  */
  58.   NULL,  /* quit_proc  */
  59.   query, /* query_proc */
  60.   run,   /* run_proc   */
  61. };
  62.  
  63.  
  64. /* These aren't really redefinable, easily. */
  65. #define IWIDTH 256
  66. #define IHEIGHT 256
  67.  
  68.  
  69. /* Global widgets'n'stuff */
  70. static guchar     *disp;      /* RGBX preview data      */
  71. static guchar     *env;          /* src warping image data */
  72. static guchar     *bump1base;
  73. static guchar     *bump1;
  74. static guchar     *bump2base;
  75. static guchar     *bump2;
  76. static guchar     *srcbump;
  77. static guchar     *destbump;
  78.  
  79. static gint       idle_tag;
  80. static GtkWidget *eventbox;
  81. static GtkWidget  *drawing_area;
  82.  
  83. static gint32      image_id;
  84. static GimpDrawable      *drawable;
  85. static GimpImageBaseType  imagetype;
  86. static guchar     *palette;
  87. static gint        ncolours;
  88.  
  89.  
  90. MAIN ()
  91.  
  92. static void
  93. query (void)
  94. {
  95.   static GimpParamDef args[] =
  96.   {
  97.     { GIMP_PDB_INT32, "run_mode", "Must be interactive (1)" },
  98.     { GIMP_PDB_IMAGE, "image", "Input Image" },
  99.     { GIMP_PDB_DRAWABLE, "drawable", "Input Drawable" },
  100.   };
  101.   static gint nargs = sizeof (args) / sizeof (args[0]);
  102.  
  103.   gimp_install_procedure("plug_in_the_slimy_egg",
  104.              "A big hello from the GIMP team!",
  105.              "Beyond help.",
  106.              "Adam D. Moss <adam@gimp.org>",
  107.              "Adam D. Moss <adam@gimp.org>",
  108.              "2000",
  109. #ifdef HIDDEN
  110.              NULL,
  111. #else
  112.              N_("<Image>/Filters/Toys/Gee-Slime"),
  113. #endif
  114.              "RGB*, INDEXED*, GRAY*",
  115.              GIMP_PLUGIN,
  116.              nargs, 0,
  117.              args, NULL);
  118. }
  119.  
  120. static void
  121. run (gchar      *name,
  122.      gint        n_params,
  123.      GimpParam  *param, 
  124.      gint       *nreturn_vals,
  125.      GimpParam **return_vals)
  126. {
  127.   static GimpParam  values[1];
  128.   GimpRunModeType   run_mode;
  129.   GimpPDBStatusType status = GIMP_PDB_SUCCESS;
  130.  
  131.   *nreturn_vals = 1;
  132.   *return_vals = values;
  133.  
  134.   run_mode = param[0].data.d_int32;
  135.  
  136.   INIT_I18N_UI();
  137.  
  138.   if (run_mode == GIMP_RUN_NONINTERACTIVE ||
  139.       n_params != 3)
  140.     {
  141.       status = GIMP_PDB_CALLING_ERROR;
  142.     }
  143.   
  144.   if (status == GIMP_PDB_SUCCESS)
  145.     {
  146.       image_id = param[1].data.d_image;
  147.       drawable = gimp_drawable_get (param[2].data.d_drawable);
  148.  
  149. #if 0
  150.       fprintf(stderr, "Got these: %d, %d, %d(%p)\n",
  151.           (int)param[0].data.d_int32,
  152.           (int)param[1].data.d_image,
  153.           (int)param[2].data.d_drawable,
  154.           drawable
  155.           );
  156. #endif
  157.  
  158.       if (drawable)
  159.     do_fun();
  160.       else
  161.     status = GIMP_PDB_CALLING_ERROR;
  162.     }
  163.  
  164.   values[0].type = GIMP_PDB_STATUS;
  165.   values[0].data.d_status = status;
  166. }
  167.  
  168.  
  169. static void
  170. build_dialog (GimpImageBaseType  basetype,
  171.           gchar             *imagename)
  172. {
  173.   GtkWidget *dlg;
  174.   GtkWidget *button;
  175.   GtkWidget *frame;
  176.   GtkWidget *frame2;
  177.   GtkWidget *vbox;
  178.   GtkWidget *hbox;
  179.   GtkWidget *hbox2;
  180.   GtkTooltips *tooltips;
  181.  
  182.   gimp_ui_init ("gee", TRUE);
  183.  
  184.   dlg = gtk_dialog_new ();
  185.   gtk_window_set_title (GTK_WINDOW (dlg),
  186.             _("GEE-SLIME"));
  187.  
  188.   gtk_window_set_position (GTK_WINDOW (dlg), GTK_WIN_POS_MOUSE);
  189.   gtk_signal_connect (GTK_OBJECT (dlg), "delete_event",
  190.               (GtkSignalFunc) window_delete_callback,
  191.               NULL);
  192.  
  193.   gimp_help_connect_help_accel (dlg, gimp_standard_help_func, "filters/geeslime.html");
  194.  
  195.   /* Action area - 'close' button only. */
  196.  
  197.   button = gtk_button_new_with_label (_("** Thank you for choosing GIMP **"));
  198.   GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
  199.   gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
  200.                  (GtkSignalFunc) window_close_callback,
  201.                  GTK_OBJECT (dlg));
  202.   gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dlg)->action_area),
  203.               button, TRUE, TRUE, 0);
  204.   gtk_widget_grab_default (button);
  205.   gtk_widget_show (button);
  206.  
  207.   tooltips = gtk_tooltips_new();
  208.   gtk_tooltips_set_tip(GTK_TOOLTIPS(tooltips), button,
  209.                _("A less-obsolete creation of Adam D. Moss / adam@gimp.org / adam@foxbox.org / 1998-2000"),
  210.                NULL);
  211.   gtk_tooltips_enable (tooltips);
  212.  
  213.   /* The 'fun' half of the dialog */
  214.     
  215.   frame = gtk_frame_new (NULL);
  216.  
  217.   gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_IN);
  218.   gtk_container_set_border_width (GTK_CONTAINER (frame), 3);
  219.   gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dlg)->vbox), frame, TRUE, TRUE, 0);
  220.  
  221.   hbox = gtk_hbox_new (FALSE, 5);
  222.   gtk_container_set_border_width (GTK_CONTAINER (hbox), 3);
  223.   gtk_container_add (GTK_CONTAINER (frame), hbox);
  224.  
  225.   vbox = gtk_vbox_new (FALSE, 5);
  226.   gtk_container_set_border_width (GTK_CONTAINER (vbox), 3);
  227.   gtk_container_add (GTK_CONTAINER (hbox), vbox);
  228.  
  229.   hbox2 = gtk_hbox_new (TRUE, 0);
  230.   gtk_container_set_border_width (GTK_CONTAINER (hbox2), 0);
  231.   gtk_box_pack_start (GTK_BOX (vbox), hbox2, FALSE, FALSE, 0);
  232.  
  233.   frame2 = gtk_frame_new (NULL);
  234.   gtk_frame_set_shadow_type (GTK_FRAME (frame2), GTK_SHADOW_ETCHED_IN);
  235.   gtk_box_pack_start (GTK_BOX (hbox2), frame2, FALSE, FALSE, 0);
  236.  
  237.   eventbox = gtk_event_box_new();
  238.   gtk_container_add (GTK_CONTAINER (frame2), GTK_WIDGET (eventbox));
  239.  
  240.   drawing_area = gtk_drawing_area_new ();
  241.   gtk_widget_set_usize (drawing_area, IWIDTH, IHEIGHT);
  242.   gtk_container_add (GTK_CONTAINER (eventbox), drawing_area);
  243.   gtk_widget_show (drawing_area);
  244.  
  245.   gtk_widget_show (eventbox);
  246.   gtk_widget_set_events (eventbox,
  247.              gtk_widget_get_events (eventbox)
  248.              | GDK_BUTTON_RELEASE_MASK);
  249.  
  250.   gtk_widget_show (frame2);
  251.  
  252.   gtk_widget_show (hbox2);
  253.  
  254.   gtk_widget_show (vbox);
  255.  
  256.   gtk_widget_show (hbox);
  257.  
  258.   gtk_widget_show (frame);
  259.  
  260.   gtk_widget_show (dlg);
  261.         
  262.   idle_tag = gtk_idle_add_priority (GTK_PRIORITY_LOW,
  263.                     (GtkFunction) iteration_callback,
  264.                     NULL);
  265.   
  266.   gtk_signal_connect (GTK_OBJECT (eventbox), "button_release_event",
  267.               GTK_SIGNAL_FUNC (toggle_feedbacktype),
  268.               NULL);
  269. }
  270.  
  271.  
  272. /* #define LIGHT 0x19
  273. #define LIGHT 0x1a
  274. #define LIGHT 0x21 */
  275. #define LIGHT 0x0d
  276. static guchar llut[256];
  277. static void
  278. gen_llut(void)
  279. {
  280.   int i,k;
  281.  
  282.   for (i=0; i<256; i++)
  283.     {
  284.       /* k = i + RINT (((double)LIGHT) * pow(((double)i / 255.0), 0.5)); 
  285.      k = i + ((LIGHT*i)/255); */
  286.       k = i + ((LIGHT*( /* (255*255)- */ i*i))/(255*255));
  287. #if 0
  288.       k = i + ((LIGHT*( /* (255*255*255)- */ i*i*i))/(255*255*255));
  289. #endif
  290.       k = k + 8;
  291.       k = (k>255 ? 255 : k);
  292.       llut[i] = k;
  293.     }
  294. }
  295.  
  296.  
  297. static void 
  298. do_fun (void)
  299. {
  300.   imagetype = gimp_image_base_type(image_id);
  301.  
  302.   if (imagetype == GIMP_INDEXED)
  303.     palette = gimp_image_get_cmap(image_id, &ncolours);
  304.   else
  305.     if (imagetype == GIMP_GRAY)
  306.       {
  307.     int i;
  308.     palette = g_malloc(256 * 3);
  309.     for (i=0; i<256; i++)
  310.       {
  311.         palette[i*3+0] =
  312.           palette[i*3+1] =
  313.           palette[i*3+2] = i;
  314.       }
  315.       }
  316.  
  317.   /* cache hint */
  318.   gimp_tile_cache_ntiles (1);
  319.  
  320.   init_preview_misc();
  321.   build_dialog(gimp_image_base_type(image_id),
  322.                gimp_image_get_filename(image_id));
  323.  
  324.   gen_llut();
  325.  
  326.   render_frame();
  327.  
  328.   gtk_main ();
  329.   gdk_flush ();
  330. }
  331.  
  332.  
  333. static void
  334. show(void)
  335. {
  336.   gdk_draw_rgb_32_image (drawing_area->window,
  337.              drawing_area->style->white_gc,
  338.              0, 0, IWIDTH, IHEIGHT,
  339.              GDK_RGB_DITHER_NORMAL,
  340.              (guchar*)disp, IWIDTH * 4);
  341. }
  342.  
  343.  
  344. /* Rendering Functions */
  345.  
  346.  
  347. static void
  348. bumpbob(int x, int y, int size)
  349. {
  350.   int o;
  351.  
  352.   /*  for (o=0; o<size; o++)
  353.     {
  354.       bump[x+(size/2)+(y+o)*IWIDTH] = 255;
  355.     }
  356.   memset(&bump[x+(y+(size/2))*IWIDTH], 255, size);
  357.   */
  358.   
  359.   for (o=0; o<size; o++)
  360.     {
  361.       int p;
  362.       for (p=0; p<size; p++)
  363.     {
  364.       int k;
  365. #define BOB_INC 45
  366.       k = destbump[p+x+(y+o)*IWIDTH] + BOB_INC;
  367.       if (k&256)
  368.         destbump[p+x+(y+o)*IWIDTH] = 255;
  369.       else
  370.         destbump[p+x+(y+o)*IWIDTH] = k;
  371.     }
  372.       /* memset(&destbump[x+(y+o)*IWIDTH], 131, size); */
  373.     }
  374. }
  375.  
  376.  
  377. /* Adam's sillier algorithm. */
  378. static void 
  379. iterate (void)
  380. {
  381.   static guint frame = 0;
  382.   gint i,j;
  383.   gint thisbump;
  384.   guchar sx,sy;
  385.   guint32 *dest;
  386.   guint32 *environment;
  387.   guchar *basebump;
  388.   unsigned int basesx;
  389.   unsigned int basesy;
  390.   /*  signed int bycxmcybx;
  391.   signed int bx2,by2;
  392.   signed int cx2,cy2;
  393.   const gint bx = -(123-128);
  394.   const gint by = (128+123);
  395.   const gint cx = by;
  396.   const gint cy = -bx;*/
  397. #define bx (-(123-128))
  398. #define by (128+123)
  399. #define cx (by)
  400. #define cy (-bx)
  401. #define bycxmcybx (by*cx-cy*bx)
  402. #define bx2 (((bx)<<19)/bycxmcybx)
  403. #define by2 (((by)<<19)/bycxmcybx)
  404. #define cx2 (((cx)<<19)/bycxmcybx)
  405. #define cy2 (((cy)<<19)/bycxmcybx)
  406.  
  407.   frame++;
  408.  
  409.   environment = (guint32*) env;
  410.   dest = (guint32*) disp;
  411.   srcbump = (frame&1) ? bump1 : bump2;
  412.   destbump = (frame&1) ? bump2 : bump1;
  413.  
  414.   /* WARP DISTORTION MAP (plughole-effect) */
  415.  
  416.   /* this setup obsolete, tranformation is constant */
  417.   /*if ((cx+bx) == 0)
  418.     cx++;
  419.     
  420.     if ((cy+by) == 0)
  421.     by++;
  422.  
  423.   bycxmcybx = (by*cx-cy*bx);
  424.  
  425.   if (bycxmcybx == 0)
  426.     bycxmcybx = 1;
  427.  
  428.   bx2 = ((bx)<<19)/bycxmcybx;
  429.   cx2 = ((cx)<<19)/bycxmcybx;
  430.   by2 = ((by)<<19)/bycxmcybx;
  431.   cy2 = ((cy)<<19)/bycxmcybx;
  432.   */
  433.  
  434.   /* A little sub-pixel jitter to liven things up. */
  435.   basesx = (((RAND_FUNC()%(29<<19)))/bycxmcybx) + ((-128-((128*256)/(cx+bx)))<<11);
  436.   basesy = (((RAND_FUNC()%(29<<19)))/bycxmcybx) + ((-128-((128*256)/(cy+by)))<<11);
  437.   
  438.   basebump = srcbump;
  439.  
  440.   
  441. #if 0
  442.   /* identity only */
  443.   j = IHEIGHT;
  444.   while (j--)
  445.     {
  446.       i = IWIDTH;
  447.       while (i--)
  448.     {
  449.       *dest++ = *environment++;
  450.     }
  451.     }
  452.   return;
  453. #endif
  454.  
  455.  
  456.   /* MELT DISTORTION MAP, APPLY IT */
  457.   j = IHEIGHT;
  458.   while (j--)
  459.     {
  460.       unsigned int tx;
  461.       unsigned int ty;
  462.       
  463.       ty = (basesy+=cx2);
  464.       tx = (basesx-=bx2);
  465.  
  466.       i = IWIDTH;
  467.       while (i--)
  468.     {
  469.       unsigned char *bptr =
  470.         (srcbump + (
  471.             (
  472.              ((255&(
  473.                 (tx>>11)
  474.                 )))
  475.              |
  476.              ((((255&(
  477.                   (ty>>11)
  478.                   ))<<8)))
  479.              )
  480.             ));
  481.  
  482.       thisbump = (11 * *(basebump) + (
  483.                       *(bptr-IWIDTH) +
  484.                       *(bptr-1) +
  485.                       *(bptr) +
  486.                       *(bptr+1) +
  487.                       *(bptr+IWIDTH)
  488.                       )
  489.               );
  490.       basebump++;
  491.       
  492.       tx += by2;
  493.       ty -= cy2;
  494.  
  495.       /* TODO: Can accelerate search for non-zero bumps with
  496.          casting an aligned long-word search. */
  497.       if (thisbump == 0)
  498.         {
  499.           *(dest++) = *( environment + (i | (j<<8) ) );
  500.           /* *(dest++) = 111; */
  501.           *(destbump++) = 0;
  502.         }
  503.       else
  504.         {
  505.           if (thisbump <= (131<<4) )
  506.         {
  507.           thisbump >>= 4;
  508.           *destbump = thisbump;
  509.         }
  510.           else
  511.         *destbump = thisbump = 131;
  512.  
  513.           /* sy = j + ( ((thisbump) - *(destbump+IWIDTH))<<1);
  514.          sx = i + ( ((thisbump) - *(++destbump))<<1);  + blah; */
  515.           sy = j + ( ((thisbump) - *(destbump+IWIDTH)));
  516.           sx = i + ( ((thisbump) - *(++destbump)));
  517.           *dest++ = *( environment + (sx | (sy<<8) ) );
  518.           /* sx = ( ((thisbump) - *(destbump+IWIDTH)));
  519.          sy = ( ((thisbump) - *(++destbump)));
  520.          *dest++ = (sx) | (sy<<8) | (sx<<16); */
  521.         }
  522.     }
  523.     }
  524.  
  525.  
  526.   srcbump = (frame&1) ? bump1 : bump2;
  527.   destbump = (frame&1) ? bump2 : bump1;
  528.   dest = (guint32 *) disp;
  529.   memset(destbump, 0, IWIDTH);
  530.  
  531. #if 1
  532.   /*  CAUSTICS!  */
  533.   /* The idea here is that we refract IWIDTH*IHEIGHT parallel rays
  534.      through the surface of the slime and illuminate the points
  535.      where they hit the backing-image.  There are some unrealistic
  536.      shortcuts taken, but the result is quite pleasing.
  537.   */
  538.   j = IHEIGHT;
  539.   while (j--)
  540.     {
  541.       i = IWIDTH;
  542.       while (i--)
  543.     {
  544.       /* Apply caustics */
  545.       sx = *(destbump++);
  546.       if (sx!=0)
  547.         {
  548.           guchar* cptr;
  549.  
  550.           sy = j + ( ((sx) - *(destbump+IWIDTH-1)));
  551.           sx = i + ( ((sx) - *(destbump)));
  552.  
  553.           /* cptr = (guchar*)((guint32*)(( dest+ (0xffff^(sx | (sy<<8) )) ))); */
  554.           cptr = (guchar*)( dest + (0xffff^(sx | (sy<<8) )) );
  555.  
  556.           *cptr = llut[*cptr]; cptr++;
  557.           *cptr = llut[*cptr]; cptr++;
  558.           *cptr = llut[*cptr];
  559.           /* this second point of light's offset (1 across, 1 down)
  560.          isn't really 'right' but it gives a more pleasing,
  561.          diffuse look. */
  562.           cptr+= 2 + IWIDTH*4;
  563.  
  564.           *cptr = llut[*cptr]; cptr++;
  565.           *cptr = llut[*cptr]; cptr++;
  566.           *cptr = llut[*cptr];
  567.         }
  568.     }
  569.     }
  570. #endif
  571.  
  572.  
  573.     /* Interactive bumpmap */
  574. #define BOBSIZE 6
  575. #define BOBSPREAD 40
  576. #define BOBS_PER_FRAME 70
  577.   destbump = (frame&1) ? bump2 : bump1;
  578.   {
  579.     gint rxp, ryp, posx, posy;
  580.     GdkModifierType mask; 
  581.     gint size, i;
  582.  
  583.     gdk_window_get_pointer (eventbox->window, &rxp, &ryp, &mask);
  584.  
  585.     for (i = 0; i < BOBS_PER_FRAME; i++)
  586.       {
  587.     size = 1 + RAND_FUNC()%BOBSIZE;
  588.  
  589.     posx = rxp + BOBSPREAD/2 -
  590.       RINT(sqrt((RAND_FUNC()%BOBSPREAD)*(RAND_FUNC()%BOBSPREAD)));
  591.     posy = ryp + BOBSPREAD/2 -
  592.       RINT(sqrt((RAND_FUNC()%BOBSPREAD)*(RAND_FUNC()%BOBSPREAD)));
  593.  
  594.     if (! ((posx>IWIDTH-size) ||
  595.            (posy>IHEIGHT-size) ||
  596.            (posx<1) ||
  597.            (posy<1) ))
  598.       bumpbob(posx, posy, size);
  599.       }
  600.   }
  601. }
  602.  
  603.  
  604. static void
  605. render_frame (void)
  606. {
  607.   static int frame = 0;
  608.  
  609. #if 0
  610.   if (frame==0)
  611.     {
  612.       gint i, bytes;
  613.       
  614.       bytes = IWIDTH*IHEIGHT*4;
  615.       
  616.       for (i=0;i<bytes;i++)
  617.     {
  618.       disp[i] = env[i];
  619.     }
  620.     }
  621. #endif
  622.  
  623.   iterate();
  624.  
  625.   show();
  626.   
  627.   frame++;
  628. }
  629.  
  630.  
  631. static void
  632. init_preview_misc (void)
  633. {
  634.   GimpPixelRgn pixel_rgn;
  635.   gint i;
  636.   gboolean has_alpha;
  637.  
  638.   has_alpha = gimp_drawable_has_alpha(drawable->id);
  639.  
  640.   env = g_malloc (4 * IWIDTH * IHEIGHT * 2);
  641.   disp = g_malloc ((IWIDTH + 2 + IWIDTH * IHEIGHT) * 4);
  642.   bump1base = g_malloc (IWIDTH * IHEIGHT + IWIDTH+IWIDTH);
  643.   bump2base = g_malloc (IWIDTH * IHEIGHT + IWIDTH+IWIDTH);
  644.  
  645.   bump1 = &bump1base[IWIDTH];
  646.   bump2 = &bump2base[IWIDTH];
  647.  
  648.   if ((drawable->width<256) || (drawable->height<256))
  649.     {
  650.       for (i=0;i<256;i++)
  651.     {
  652.       if (i < drawable->height)
  653.         {
  654.           gimp_pixel_rgn_init (&pixel_rgn,
  655.                    drawable,
  656.                    drawable->width>256?
  657.                    (drawable->width/2-128):0,
  658.                    (drawable->height>256?
  659.                    (drawable->height/2-128):0)+i,
  660.                    MIN(256,drawable->width),
  661.                    1,
  662.                    FALSE,
  663.                    FALSE);
  664.           gimp_pixel_rgn_get_rect (&pixel_rgn,
  665.                        &env[(256*i +
  666.                          (
  667.                           (
  668.                            drawable->width<256 ?
  669.                            (256-drawable->width)/2 :
  670.                            0
  671.                            )
  672.                           +
  673.                           (
  674.                            drawable->height<256 ?
  675.                            (256-drawable->height)/2 :
  676.                            0
  677.                            ) * 256
  678.                           )) *
  679.                        gimp_drawable_bpp
  680.                        (drawable->id)
  681.                        ],
  682.                        drawable->width>256?
  683.                        (drawable->width/2-128):0,
  684.                        (drawable->height>256?
  685.                        (drawable->height/2-128):0)+i,
  686.                        MIN(256,drawable->width),
  687.                        1);
  688.         }
  689.     }
  690.     }
  691.   else
  692.     {
  693.       gimp_pixel_rgn_init (&pixel_rgn,
  694.                drawable,
  695.                drawable->width>256?(drawable->width/2-128):0,
  696.                drawable->height>256?(drawable->height/2-128):0,
  697.                MIN(256,drawable->width),
  698.                MIN(256,drawable->height),
  699.                FALSE,
  700.                FALSE);
  701.       gimp_pixel_rgn_get_rect (&pixel_rgn,
  702.                    env,
  703.                    drawable->width>256?(drawable->width/2-128):0,
  704.                    drawable->height>256?(drawable->height/2-128):0,
  705.                    MIN(256,drawable->width),
  706.                    MIN(256,drawable->height));
  707.     }
  708.  
  709.   gimp_drawable_detach(drawable);
  710.  
  711.  
  712.   /* convert the image data of varying types into flat grey or rgb. */
  713.   switch (imagetype)
  714.     {
  715.     case GIMP_GRAY:
  716.     case GIMP_INDEXED:
  717.       if (has_alpha)
  718.     {
  719.       for (i=IWIDTH*IHEIGHT;i>0;i--)
  720.         {
  721.           env[4*(i-1)+2] =
  722.         ((palette[3*(env[(i-1)*2])+2]*env[(i-1)*2+1])/255)
  723.         + ((255-env[(i-1)*2+1])*((i&255) ^ (i>>8)))/255;
  724.           env[4*(i-1)+1] =
  725.         ((palette[3*(env[(i-1)*2])+1]*env[(i-1)*2+1])/255)
  726.         + ((255-env[(i-1)*2+1])*((i&255) ^ (i>>8)))/255;
  727.           env[4*(i-1)+0] =
  728.         ((palette[3*(env[(i-1)*2])+0]*env[(i-1)*2+1])/255)
  729.         + ((255-env[(i-1)*2+1])*((i&255) ^ (i>>8)))/255;
  730.         }
  731.     }
  732.       else
  733.     {
  734.       for (i=IWIDTH*IHEIGHT;i>0;i--)
  735.         {
  736.           env[4*(i-1)+2] = palette[3*(env[i-1])+2];
  737.           env[4*(i-1)+1] = palette[3*(env[i-1])+1];
  738.           env[4*(i-1)+0] = palette[3*(env[i-1])+0];
  739.         }
  740.     }
  741.       break;
  742.  
  743.     case GIMP_RGB:
  744.       if (has_alpha)
  745.     {
  746.       for (i=0;i<IWIDTH*IHEIGHT;i++)
  747.         {
  748.           env[i*4+2] =
  749.         (env[i*4+2]*env[i*4+3])/255
  750.         + ((255-env[i*4+3])*((i&255) ^ (i>>8)))/255;
  751.           env[i*4+1] =
  752.         (env[i*4+1]*env[i*4+3])/255
  753.         + ((255-env[i*4+3])*((i&255) ^ (i>>8)))/255;
  754.           env[i*4+0] =
  755.         (env[i*4+0]*env[i*4+3])/255
  756.         + ((255-env[i*4+3])*((i&255) ^ (i>>8)))/255;
  757.         }
  758.     }
  759.       else
  760.     {
  761.       for (i=IWIDTH*IHEIGHT;i>0;i--)
  762.         {
  763.           env[4*(i-1)+2] = env[(i-1)*3+2];
  764.           env[4*(i-1)+1] = env[(i-1)*3+1];
  765.           env[4*(i-1)+0] = env[(i-1)*3+0];
  766.         }
  767.     }
  768.       break;
  769.  
  770.     default:
  771.       break;
  772.     }
  773.  
  774.   /* Finally, 180-degree flip the environmental image! */
  775.   for (i = 0; i < IWIDTH*IHEIGHT/2; i++)
  776.     {
  777.       guchar t;
  778.       t = env[4*(i)+0];
  779.       env[4*(i)+0] = env[4*(IWIDTH*IHEIGHT-(i+1))+0];
  780.       env[4*(IWIDTH*IHEIGHT-(i+1))+0] = t;
  781.       t = env[4*(i)+1];
  782.       env[4*(i)+1] = env[4*(IWIDTH*IHEIGHT-(i+1))+1];
  783.       env[4*(IWIDTH*IHEIGHT-(i+1))+1] = t;
  784.       t = env[4*(i)+2];
  785.       env[4*(i)+2] = env[4*(IWIDTH*IHEIGHT-(i+1))+2];
  786.       env[4*(IWIDTH*IHEIGHT-(i+1))+2] = t;
  787.     }
  788. }
  789.  
  790.  
  791.  
  792. /* Util. */
  793.  
  794. static int
  795. do_iteration (void)
  796. {
  797.   render_frame ();
  798.   show ();
  799.  
  800.   return 1;
  801. }
  802.  
  803.  
  804.  
  805. /*  Callbacks  */
  806.  
  807. static gint
  808. window_delete_callback (GtkWidget *widget,
  809.                 GdkEvent  *event,
  810.                 gpointer   data)
  811. {
  812.   gtk_idle_remove (idle_tag);
  813.  
  814.   gdk_flush ();
  815.   gtk_main_quit ();
  816.  
  817.   return FALSE;
  818. }
  819.  
  820. static void
  821. window_close_callback (GtkWidget *widget,
  822.                        gpointer   data)
  823. {
  824.   if (data)
  825.     gtk_widget_destroy(GTK_WIDGET(data));
  826.  
  827.   window_delete_callback (NULL, NULL, NULL);
  828. }
  829.  
  830. static void
  831. toggle_feedbacktype (GtkWidget *widget,
  832.              gpointer   event)
  833. {
  834.  
  835. }
  836.  
  837.  
  838. static gint
  839. iteration_callback (gpointer   data)
  840. {
  841.   do_iteration();
  842.  
  843.   return TRUE;
  844. }
  845.